/*
 * Copyright 2015 泛泛o0之辈
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.jfast.framework.jdbc.orm;

import cn.jfast.framework.base.util.ClassUtils;
import cn.jfast.framework.base.util.StringUtils;
import cn.jfast.framework.jdbc.annotation.*;
import cn.jfast.framework.jdbc.db.ConnectionFactory;
import cn.jfast.framework.jdbc.orm.sql.*;
import cn.jfast.framework.log.LogFactory;
import cn.jfast.framework.log.LogType;
import cn.jfast.framework.log.Logger;
import javassist.*;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.sql.Connection;

public class DaoProxy implements MethodInterceptor {
    private Object daoProxy;
    private ClassPool cp = ClassPool.getDefault();
    private CtClass clazz;
    private CtMethod ctMethod;
    private Type[] paramTypes;
    private String[] paramNames;
    private Select select;
    private Update update;
    private Insert insert;
    private Delete delete;
    private Type returnType;
    private Executor sql;
    private Connection conn;
    private Logger log = LogFactory.getLogger(LogType.JFast, DaoProxy.class);

    public Object getInstance(Object dao){
        this.daoProxy = dao;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.daoProxy.getClass());
        enhancer.setCallback(this);
        cp.insertClassPath(new ClassClassPath((daoProxy.getClass())));
        try {
            clazz = cp.getCtClass(daoProxy.getClass().getName());
        } catch (NotFoundException e) {
            log.error("", e);
        }
        return enhancer.create();
    }

    public synchronized Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        conn = ConnectionFactory.getThreadLocalConnection();
        conn.setCatalog(daoProxy.getClass().getName()+"."+method.getName());
        conn.setReadOnly(method.isAnnotationPresent(NoAutoLog.class));;
        if(method.isAnnotationPresent(Body.class)
                ||daoProxy.getClass().isAnnotationPresent(Body.class))
            return method.invoke(daoProxy, objects);
        paramTypes = method.getParameterTypes();
        CtClass[] ctClasses = new CtClass[paramTypes.length];
        for(int i=0; i<paramTypes.length; i++){
        	ctClasses[i] = cp.getCtClass(((Class<?>)paramTypes[i]).getName());
        }
        ctMethod = clazz.getDeclaredMethod(method.getName(), ctClasses);
        paramNames = ClassUtils.getMethodParamNames(ctMethod);
        if(null == paramNames) {
            paramNames = new String[paramTypes.length];
            Object[][] annotations = ctMethod.getAvailableParameterAnnotations();
            for (int i = 0; i < paramTypes.length; i++) {
                // 获取方法参数的属性
                for (Object anno : annotations[i]) {
                    if (anno instanceof SqlParam) {
                        SqlParam sqlParam = (SqlParam) anno;
                        if(StringUtils.isNotEmpty(sqlParam.name())){
                            paramNames[i] = sqlParam.name();
                        }
                    }
                }
            }
        }
        returnType = method.getGenericReturnType();
        if(method.isAnnotationPresent(Select.class)) {
            select = method.getAnnotation(Select.class);
            sql = new SelectSql(paramTypes,paramNames,objects,select,returnType,method);
        } else if(method.isAnnotationPresent(Update.class)) {
            update = method.getAnnotation(Update.class);
            sql = new UpdateSql(paramTypes,paramNames,objects,update,returnType,method);
        } else if(method.isAnnotationPresent(Insert.class)) {
            insert = method.getAnnotation(Insert.class);
            sql = new InsertSql(paramTypes,paramNames,objects,insert,returnType,method);
        } else if(method.isAnnotationPresent(Delete.class)) {
            delete = method.getAnnotation(Delete.class);
            sql = new DeleteSql(paramTypes,paramNames,objects,delete,returnType,method);
        } else {
            sql = new NamedSql(paramTypes,paramNames,objects,method.getName(),returnType,method);
        }
        return sql.execute();
    }

}
